﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using PS.WssSync.WebPartService;
using PS.Utils.FPRPC;

namespace PS.WssSync.Utils
{
    public class WebHelper
    {        
        private readonly WebPartPagesWebService wpService;
        private readonly FrontPageRPC frontPage;
        private const string serviceUrl = "/_vti_bin/WebPartPages.asmx";
        private readonly string[] badExtensions = 
            {".metainfo", ".md5", ".config", ".options", ".db", ".exe", ".pdb", ".dll", ".bat" };

        public WebHelper()
        {
            wpService = new WebPartPagesWebService();
            wpService.Credentials = CredentialCache.DefaultCredentials;
            frontPage = new FrontPageRPC();
        }

        public WebHelper(ICredentials credentials) : this()
        {                        
            wpService.Credentials = credentials;            
        }

        public string GetDocument(WebUrl webUrl)
        {
            SetServiceUrl(webUrl);
            string response = wpService.GetWebPartPage(webUrl.FileUrl, SPWebServiceBehavior.Version3);            
            return ParseResponse(response);
        }

        public void RemoveAllWebParts(WebUrl webUrl)
        {            
            string response = GetDocument(webUrl);
            IEnumerable<Guid> wpIDs = GetWebPartIDs(response);
            foreach (Guid wpID in wpIDs)
            {
                RemoveWebPart(wpID, webUrl);
            }
        }        

        public void RemoveWebPart(Guid wpGuid, WebUrl webUrl)
        {
            string url = UrlFromWebUrl(webUrl);                                    
            wpService.DeleteWebPart(url, wpGuid, Storage.None);
        }

        public static string GetFileExtension(WebUrl webUrl)
        {
            string fileUrl = webUrl.FileUrl;
            int index = fileUrl.LastIndexOf(".");
            if(-1 == index)
            {
                string message = String.Format("File extension not found in the url {0}", fileUrl);
                throw new ApplicationException(message);
            }
            return fileUrl.Substring(index, fileUrl.Length - index);
        }        

        public bool EqualsWpPages(string page1, string page2)
        {
            return String.Equals(CleanWebPartPage(page1), CleanWebPartPage(page2), StringComparison.Ordinal);
        }

        public bool IsWebPartPage(string fileName)
        {
            return fileName.EndsWith(".aspx") || fileName.EndsWith(".master");
        }

        public bool IsRequiredFile(WssSyncConfig syncConfig, WebUrl url)
        {            
            if(url.FileUrl.Contains(".svn")||badExtensions.Contains(GetFileExtension(url)))
            {
                return false;
            }

            if(null == syncConfig)
            {
                return true;
            }
            
            foreach(Filter filter in syncConfig.AllowFilters)
            {
                if(!ProcessFilter(url, filter))
                {
                    return false;
                }
            }

            foreach(Filter filter in syncConfig.IgnoreFilters)
            {
                if(ProcessFilter(url, filter))
                {
                    return false;
                }
            }

            return true;
        }

        private static bool ProcessFilter(WebUrl url, Filter filter)
        {
            string[] filterData = filter.Data.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

            if(filter is Extension)
            {
                string extension = GetFileExtension(url);
                return filterData.Contains(extension);
            }
            if(filter is Folder)
            {
                const char slash = '/';
                const char backSlash = '\\';

                if(url.FileUrl.Contains(slash))
                {
                    string folder = url.FileUrl.Substring(0, url.FileUrl.LastIndexOf(slash));                        

                    foreach(string data in filterData)
                    {
                        string trimmedData = data.Trim(new[] {slash, backSlash});
                        string slashFolder = trimmedData.Replace(backSlash, slash);
                        string backSlashFolder = trimmedData.Replace(slash, backSlash);
                        
                        if(folder.Equals(slashFolder) || folder.Equals(backSlashFolder))
                        {
                            return true;
                        }
                    }
                }
                else
                {
                    return filterData.Contains(".");
                }
            }
            if(filter is Regexp)
            {
                foreach(string pattern in filterData)
                {
                    if(Regex.IsMatch(url.FileUrl, pattern))
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        private void SetServiceUrl(WebUrl webUrl)
        {
            wpService.Url = webUrl.SiteUrl.TrimEnd('/') + serviceUrl;
        }

        private static IEnumerable<Guid> GetWebPartIDs(string source)
        {
            List<Guid> result = new List<Guid>();

            if (!string.IsNullOrEmpty(source))
            {
                Regex rg = new Regex("WebPartId=\"{([0-9A-F-]*)}\"");

                foreach (Match match in rg.Matches(source))
                {                                         
                    Guid guid = new Guid(match.Groups[1].Value);
                    result.Add(guid);
                }                
            }

            return result;
        }

        public string RemoveWebPartsFromText(string source, out ICollection<string> removedWebParts)
        {
            removedWebParts = new List<string>();
            StringBuilder sb = new StringBuilder(source);
                                   
            string pattern = "<\\ *([A-Za-z0-9:]+)[^><]*WebPartId=\"{[0-9A-F-]*}\"[^><]*>";            
            
            var tagNames = new List<string>();
            foreach (Match  match in Regex.Matches(source, pattern, RegexOptions.Singleline))
            {
                tagNames.Add(match.Groups[1].Value);
            }
                               
            foreach(string tagName in tagNames)
            {
                pattern = String.Format("<\\ *{0}[^><]*WebPartId=\"{{[0-9A-F-]*}}\"[^><]*>(.*<\\ */\\ *{0}\\ *>)?", tagName);                
                foreach(Match match in Regex.Matches(source, pattern, RegexOptions.Singleline))
                {
                    sb = sb.Replace(match.Value, string.Empty);
                    removedWebParts.Add(match.Value);
                }
            }
            
            return sb.ToString();
        }

        private static string UrlFromWebUrl(WebUrl webUrl)
        {
            return webUrl.SiteUrl.TrimEnd('/') + "/" + webUrl.FileUrl;
        }

        private static string ParseResponse(string response)
        {
            string delimiter = -1 != response.IndexOf("<HasByteOrderMark/>") ? "<HasByteOrderMark/>" : "</html>\n";                                   
            return response.Substring(response.IndexOf(delimiter) + delimiter.Length);
        }        

        private static string CleanWebPartPage(string pageText)
        {
            const string pattern1 = "__designer:[^\"]*\"[^\"]*\"";
            const string pattern2 = "__Preview=\"[^\"]*\"";

            string result = Regex.Replace(pageText, pattern1, string.Empty, RegexOptions.Singleline);
            result = Regex.Replace(result, pattern2, string.Empty, RegexOptions.Singleline);

            return result;
        }

        public string GetWebPart(string pageUrl, Guid storageKey, Storage storage)
        {
            SetServiceUrl(frontPage.UrlToWebUrl(pageUrl));            
            return wpService.GetWebPart(pageUrl, storageKey, storage);
        }        

        public string GetWebpart2(string pageUrl, Guid storageKey, Storage storage, SPWebServiceBehavior webServiceBehavior)
        {
            SetServiceUrl(frontPage.UrlToWebUrl(pageUrl));
            return wpService.GetWebPart2(pageUrl, storageKey, storage, webServiceBehavior);
        }

        public void AddWebPart(string pageUrl, string webPartXml, Storage storage)
        {
            SetServiceUrl(frontPage.UrlToWebUrl(pageUrl));
            wpService.AddWebPart(pageUrl, webPartXml, storage);
        }
    }
}
